﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Reflector.CodeModel;
using Reflector.Graph.Graphs;

namespace MethodSeqGenerator
{
    /*
     * An object that holds method invocation objects
     * Major issue is that actual MethodInvokeExpression inherits from IExpression
     * and the graph primarily deals with IStatement. This class is introduced
     * as a workaround
     */ 
    class MethodInvocationHolder : IStatement
    {
        public TypeHolder referenceClsName;
        public TypeHolder returnType;
        public String argumentString = "";
        public List<TypeHolder> argStringAr;
        public String methodName;
	    int noOfArguments;
        
        public bool bStatic = false;        //True/False if the method call is static/non-static
        public bool bPropertyReference = false;     //This is set to true if the MethodInvocationHolder stores "Objref.field"
        public String propertyReferenceValue = "";   //When this method invocation represents a property reference of primitive types, this field contains value for that field

        public int nestingDepth;       //This flag will be set to true if the method invocation
                                                //is ever found in a loop



        //Create method invocation holder from constructor call
        public MethodInvocationHolder(IObjectCreateExpression oceExpr)
        {
            IMethodReference methodReference = oceExpr.Constructor;
            String typeNameNew = SeqGenUtil.GetFullTypeName(methodReference.DeclaringType);                
            this.referenceClsName = new TypeHolder(typeNameNew);
            this.methodName = "CONSTRUCTOR";
            this.returnType = new TypeHolder(typeNameNew);

            loadArguments(oceExpr.Arguments, methodReference.Parameters);
        }
        
        //Create a method invocation from a normal function call
        public MethodInvocationHolder(IMethodInvokeExpression miExpr)
        {
            IMethodReferenceExpression methodReferenceExpr = miExpr.Method as IMethodReferenceExpression;
            IMethodReference methodReference = methodReferenceExpr.Method;
            this.methodName = methodReference.Name;
            //this.arguments = methodReference.Parameters.ToString();
            this.returnType = new TypeHolder(SeqGenUtil.GetFullTypeName(methodReference.ReturnType.Type));

            IExpression exprObj = methodReferenceExpr.Target;
            this.referenceClsName = SeqGenUtil.ExtractTypeHolder(exprObj);

            if (exprObj is ITypeReferenceExpression)
            {
                bStatic = true;
            }

            loadArguments(miExpr.Arguments, methodReference.Parameters);
        }

        //Create a method invocation holder from a IPropertyReferenceExpression such as "objRef.name"
        public MethodInvocationHolder(IPropertyReferenceExpression value)
        {
            bPropertyReference = true;
            IPropertyReference iprObj = value.Property;
            IExpression receiverObj = value.Target;                 

            IPropertyDeclaration ipdObj = iprObj.Resolve();
            this.returnType = new TypeHolder(SeqGenUtil.GetFullTypeName(iprObj.PropertyType));
            this.methodName = ipdObj.Name;
            this.referenceClsName = SeqGenUtil.ExtractTypeHolder(receiverObj);

            argStringAr = new List<TypeHolder>(0);
            noOfArguments = 0;
        }

        public override String ToString()
        {
            if (!bPropertyReference)
                return this.referenceClsName.getCompleteExprName() + "." + this.methodName + "(" + argumentString + ")" + ":" + this.returnType;
            else
                return this.referenceClsName.getCompleteExprName() + "." + this.methodName + ":" + this.returnType + "(" + this.propertyReferenceValue + ")";
        }

        //Collect types from the method-call expression:
        //  Advantages: Retrieve actual values and also get actual types, but not all types are available
        //Collect types from the method-declaration
        //  Advantages: Retrieve general types but values are not possible
        //Current Implementation: Use method-call first, if not possible, uses method-declaration
        private void loadArguments(IExpressionCollection iecObj, IParameterDeclarationCollection paramObj)
        {
            argStringAr = new List<TypeHolder>(iecObj.Count);
            noOfArguments = iecObj.Count;
            if (iecObj != null && iecObj.Count > 0)
            {
                int paramCount = 0;
                foreach (IExpression expression in iecObj)
                {
                    TypeHolder argTypHolder = SeqGenUtil.ExtractTypeHolder(expression);
                    
                    if(argTypHolder.getTypeName().Equals("UNKNOWN")) 
                    {
                        //argTypHolder.setTypeName(SeqGenUtil.GetFullTypeName(paramObj[paramCount].ParameterType));                        
                    }

                    argStringAr.Add(argTypHolder);
                    argumentString = argumentString + argTypHolder.getTypeName() +
                        "(" + argTypHolder.getVarName() + ")" + ",";                    

                    paramCount++;
                }
            }
        }

        public override bool Equals(object obj)
        {
            MethodInvocationHolder mih1 = this;
            MethodInvocationHolder mih2 = (MethodInvocationHolder) obj;

            if (!mih1.referenceClsName.Equals(mih2.referenceClsName))
            {
                return false;
            }

            if (!mih1.methodName.Equals(mih2.methodName))
            {
                return false;
            }

            if (mih1.noOfArguments != mih2.noOfArguments)
            {
                return false;
            }

            return true;
        }
    }   
}
